/*
 * heres the basics:  We need a program to move the template from a bunch of gene pages 
 * and put them into a special location: basically arranged by entrez gene id.
 * We then leave a small linker template behind to effectively wrap the messy template.
 * 
 * Since we are editing wikipedia, we need to log in as the bot. 
 * 
 * basically, we will read in a list of gene pages.  For each page:
 * 
 * 1 Determine if the page exists.  Abort that page if it doesn't
2 Determine if a template exists on a page and parse the Entrez Gene ID.  Abort page
 *   if niether can be found, then abort processing of this page.
 *   All page aborts should be logged in an error output.
 * 
 * 3 Remove and store the gnf template from the page data.  
 * 4 at the top of the page, add in a small link to the linker template
 *   {{Linker | geneid = XXXXX}}
 * 5 Determine if a page exists at Template:PBB/XXX ,where XXX is the geneid
 * 6 if no pages exists, then create a new page and upload the extracted gnf template.
 * 7 If the page is created successfully, then upload the old page with the changed data
 * 
 * Remember, we must wait 6 seconds before each post. And if we error at any time, we abort that
 * gene and move to the next one.
 * 
 * after all this processing, move to the next gene. and output the gene and ID to a success file.
 * 
 */
package templatemover;

import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author jhuss
 */
public class tmMain {
    //Going to hardcode a couple of output files.  Good times.
    public static final String OUTPATH_ERROR = "c:/tmover/err.out";
    public static final String OUTPATH_SUCCESS = "c:/tmover/success.out";
    public static final String GNF_BOX = "GNF_Protein_box";
    public static final String ORTHO_BOX = "GNF_Ortholog_box";
    public static final String CONTROL_BOX = "PBB_Controls";
    public static final String GENE_ID_KEY = "Hs_EntrezGene";
    public static final String PBB_TEMPLATE_PAGE_PREFIX = "Template:PBB/";
    
    public static final String COMMENT_PROT_BOX = "<!-- The GNF_Protein_box is automatically maintained by Protein Box Bot.  See Template:PBB_Controls to Stop updates. -->";
    public static final String COMMENT_CONTROL_BOX = "<!-- The PBB_Controls template provides controls for Protein Box Bot, please see Template:PBB_Controls for details. -->";
    
    
    private BufferedWriter outErr;
    private BufferedWriter outSuc;
    private BufferedReader in;
    private tmComm wikiCom;
    public static final boolean debugUpload = true;
    
    private String lineSep;

    @SuppressWarnings("static-access")
    public tmMain(String input) {
        lineSep = System.getProperty("line.separator");
        try {

            in = new BufferedReader(new FileReader(input));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Error while initializing input, " + input);
            return;
        }


        try {
            //Contructor, expects a string path to the location of the input file.
            //Everything spawns from here..
            
            //Append to files, don't overwrite them.
            outErr = new BufferedWriter(new FileWriter(this.OUTPATH_ERROR, true));
            outSuc = new BufferedWriter(new FileWriter(this.OUTPATH_SUCCESS, true));

        } catch (IOException ex) {
            Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Error while initializing output");
            return;
        }


        //Ok, now create the wiki comm..

        wikiCom = new tmComm("ProteinBoxBot", "gnf1sdsu", "http://en.wikipedia.org/w/");

        String page = null;
        int count = 0;
        try {
            //Now we need to loop through all the pages..
            while ((page = in.readLine()) != null) {
                outErr.flush();
                outSuc.flush();
                count++;
                System.out.println("**** Starting Page "+count +", " + page );
                page = page.trim();
                String pageEntID = "";
                String pageData = "";
                //Retrieve page from Wikipedia.
                try {
                    pageData = wikiCom.downloadPage(page);
                } catch (Exception ex) {
                    Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
                    outErr.write(page + "\t" + pageEntID + "\t Page d/l Error.\n");
                    continue;
                }
                //ok, lets check to make sure the page is Good.

                if (pageData == null) {
                    outErr.write(page + "\t" + pageEntID + "\t Page is null.\n");
                    continue;
                }
                if (pageData.compareTo("") == 0) {
                    outErr.write(page + "\t" + pageEntID + "\t Page is blank.\n");
                    continue;
                }

                //ok, looks like we have data on the page.  Now we need to see if there is a 
                //protein box to parse.

                @SuppressWarnings("static-access")
                String[] splitPageData = this.find_Template(pageData, this.GNF_BOX);
                if (splitPageData == null) {
                    //Didn't find a protein box, so we need to error here.
                    outErr.write(page + "\t" + pageEntID + "\t No template Found.\n");
                    continue;
                }

                //Ok, now we need to find the entrez Gene ID. 
                //First we are going to find the ortholog box in the template.
                @SuppressWarnings("static-access")
                String[] orthologBoxData = this.find_Template(pageData, this.ORTHO_BOX);
                if (orthologBoxData == null) {
                    //Didn't find a protein box, so we need to error here.
                    outErr.write(page + "\t" + pageEntID + "\t No Ortholog Box Found.\n");
                    continue;
                }

                //Ok, now that we have the box, we want to parse it. 
                Hashtable<String, String> orthoInfo = parseBox(orthologBoxData[1]);
                String geneID = "";
                if (!orthoInfo.containsKey(this.GENE_ID_KEY)) {
                    outErr.write(page + "\t" + pageEntID + "\t No Gene ID Found.\n");
                    continue;
                } else {
                    geneID = orthoInfo.get(this.GENE_ID_KEY);
                }

                //Good News, now we have a Gene ID to use! 
                //We now need to check and make sure that the template page does not already exist...
                String templatePage = "";
                try {
                    templatePage = wikiCom.downloadPage(this.PBB_TEMPLATE_PAGE_PREFIX + geneID);
                } catch (Exception ex) {
                    Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
                    outErr.write(page + "\t" + pageEntID + "\t Template Page d/l Error.\n");
                    continue;
                }

                if (templatePage != null) {
                    if (templatePage.compareTo("") != 0) {
                        
                        if (templatePage.trim().compareTo(splitPageData[1].trim()) != 0){
                        
                        //ok, we have something already there - we need to error.
                        outErr.write(page + "\t" + pageEntID + "\t Template Page Already Exists.\n");
                        continue;
                        }
                    }
                }

                //Ok, we have a blank/ non-existant template page. We need to upload a new template page.
                try {
                    wikiCom.uploadPage(this.PBB_TEMPLATE_PAGE_PREFIX + geneID, splitPageData[1], "Created Template page for " + page);
                } catch (Exception ex) {
                    Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
                    outErr.write(page + "\t" + pageEntID + "\t Template Page u/l Error.\n");
                    continue;
                }

                //ok, page has been supposedly created.  now we need to make sure it is there..

                try {
                    templatePage = wikiCom.downloadPage(this.PBB_TEMPLATE_PAGE_PREFIX + geneID);
                } catch (Exception ex) {
                    Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
                    outErr.write(page + "\t" + pageEntID + "\t Template Page d/l Error.\n");
                    continue;
                }

                if (templatePage == null) {

                    //ok, we have something already there - we need to error.
                    outErr.write(page + "\t" + pageEntID + "\t Template Page was not created.\n");
                    continue;

                }
                if (templatePage.compareTo("") == 0) {
                    //ok, we have something already there - we need to error.
                    outErr.write(page + "\t" + pageEntID + "\t Template Page was not created.\n");
                    continue;
                }
                
                //Alright, looks like we created the page successfully.  now all we have left to do is modify the 
                //gene page and put it back on wikipedia.
                
                //Things we want to change: 
                //Move the Control template to the bottom of the file. 
                //Replace the gnf_template with {{PBB|geneid=XXXX}}
                //move the comments appropriately too.. 
                
                //start with replacing the template... 
                String newPage = splitPageData[0] + "{{PBB|geneid="+geneID+"}}" + splitPageData[2];
                String tmpPage = "";
                //Ok, now we want to remove the template page comment if it exists..
                int textIndex;
                if ( (textIndex = newPage.indexOf(this.COMMENT_PROT_BOX)) != -1){
                    tmpPage = newPage.substring(0,textIndex);
                    tmpPage = tmpPage + newPage.substring(textIndex + this.COMMENT_PROT_BOX.length());
                    newPage = tmpPage;
                }
                
                //now look for the control template...
                String[] tmpParts = find_Template(newPage,this.CONTROL_BOX);
                if (tmpParts != null) {
                    //Ok, looks like we need to move the control box..
                    tmpPage = tmpParts[0];
                    tmpPage += tmpParts[2];
                    String tmpPage2 = "";
                    //if we found the control template, there might be a comment to move too.
                    if ( (textIndex = tmpPage.indexOf(this.COMMENT_CONTROL_BOX)) != -1){
                        tmpPage2 = tmpPage.substring(0,textIndex);
                        tmpPage2 = tmpPage2 + tmpPage.substring(textIndex + this.COMMENT_CONTROL_BOX.length());
                        tmpPage = tmpPage2 + lineSep + this.COMMENT_CONTROL_BOX;
                    }
                    tmpPage = tmpPage  + lineSep + tmpParts[1];
                    newPage = tmpPage;
                    
                }
                
                newPage = newPage.trim();
                
                //Ok, should have all the modifications made - now we upload the altered page.
                try {
                    wikiCom.uploadPage(page, newPage, "Replaced protein Box Template with PBB Template for easy viewing. ");
                } catch (Exception ex) {
                    Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
                    outErr.write(page + "\t" + pageEntID + "\t New Page u/l Error.\n");
                    continue;
                }
                
                
                

                //If we get here, all went well and we have updated everything.
                outSuc.write(page + "\t" + pageEntID + "\n");
            }//End While (Each Page)
            
        outErr.flush();
        outSuc.flush();
        outErr.close();
        outSuc.close();
        } catch (IOException ex) {
            Logger.getLogger(tmMain.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("IO Error. Aborting");
            return;
        }
        
        




    }    //All the info needed to parse a template box.
    
    
    
    public static final String BOX_TITLE_KEY = "|||TITLE_KEY|||_1dggusy_35b338fbfb__|||";
    public static final String BOX_ERROR_KEY = "|||ERROR_KEY|||_HUEUIQnnfuue_773jjf983___|||";
    public static final String NO_ERRORS = "0";
    public static final String YES_ERRORS = "1";

    private Hashtable<String, String> parseBox(String box) {
        //requires that the box include {{ and }} at the start and end.
        //Also, this will only parse for the box outside.

        int braceCount = 0;
        int charIndex = 0;
        Hashtable<String, String> data = new Hashtable<String, String>();
        data.put(this.BOX_ERROR_KEY, this.NO_ERRORS);

        int boxStart = box.indexOf("{{");
        if (boxStart != 0) {
            data.put(this.BOX_ERROR_KEY, this.YES_ERRORS);
            return data; //no box found.
        }
        braceCount = 1; //1 pair of braces so far..
        int titleEnd = box.indexOf("|", boxStart + 2);
        int boxEnd = box.lastIndexOf("}}");
        if (boxEnd + 2 != box.length()) {
            //this box has no end - error and leave.
            data.put(this.BOX_ERROR_KEY, this.YES_ERRORS);
            return data;
        }
        //error catching
        if (titleEnd == -1) {
            //nothing in the box. Look for the end brace.
            titleEnd = box.indexOf("}}", boxStart + 2);
            if (titleEnd == -1) {
                //this box has no end - need to raise an error.
                data.put(this.BOX_ERROR_KEY, this.YES_ERRORS);
                return data;
            } else {
                data.put(this.BOX_TITLE_KEY, box.substring(boxStart + 2, titleEnd - 1).trim());
                return data;
            }
        }
        data.put(this.BOX_TITLE_KEY, box.substring(boxStart + 2, titleEnd - 1).trim());
        //Now have the title.

        int nextKeyS = titleEnd + 1;
        int nextKeyE = titleEnd + 1;

        int nextValS = titleEnd + 1;
        int nextValE = titleEnd + 1;

        boolean inData = false;
        boolean inKey = true;


        for (int i = titleEnd + 1; i < boxEnd; i++) {
            String tmp = box.substring(i, i + 1);
            String tmp2 = box.substring(i, i + 2);
            if (tmp2.compareTo("{{") == 0 || tmp2.compareTo("[[") == 0 || tmp2.compareTo("{|") == 0) {
                //We have opened another brace - we can only gather an attribute if the brace count is
                //equal to 2.
                braceCount++;
                i++; //skip an i
                continue;
            }

            if (tmp2.compareTo("}}") == 0 || tmp2.compareTo("]]") == 0 || tmp2.compareTo("|}") == 0) {
                //We have closed another brace - we can only gather an attribute if the brace count is
                //equal to 2.
                braceCount--;
                i++; //skip an i
                continue;
            }

            if (braceCount < 1) {
                //Error - box seemed to end early.
                data.put(this.BOX_ERROR_KEY, this.YES_ERRORS);
                return data;
            }
            //we only deal with the outermost box, so we'll have to be in BraceCount 1 to gather keys or vals..'
            if (braceCount == 1) {
                //we have two options - we can either be in a key, or in a value.
                //we could also be ending a key or ending a value.
                if (inKey) {
                    //So we are gathering up a key
                    if (tmp.compareTo("=") == 0) {
                        //we have found the end of the key.
                        nextKeyE = i - 1;
                        nextValS = i + 1;
                        inKey = false;
                        inData = true;
                        continue;
                    }
                    //error values:
                    if (tmp.compareTo("|") == 0) {
                        //error
                        data.put(this.BOX_ERROR_KEY, this.YES_ERRORS);
                        return data;
                    }

                }
                if (inData) {
                    //So we are gathering up a value for a key.
                    if (tmp.compareTo("|") == 0) {
                        //so we are not in any subtemplate, which means that we just ended the data.
                        nextValE = i - 1;

                        data.put(box.substring(nextKeyS, nextKeyE).trim(), box.substring(nextValS, nextValE).trim());

                        inKey = true;
                        inData = false;
                        nextKeyS = i + 1;
                        nextKeyE = i + 1;
                        nextValS = i + 1;
                        nextValE = i + 1;

                    } else if (i == boxEnd - 1) {
                        //last data value.
                        nextValE = i;

                        data.put(box.substring(nextKeyS, nextKeyE).trim(), box.substring(nextValS, nextValE).trim());

                        inKey = true;
                        inData = false;
                        nextKeyS = i + 1;
                        nextKeyE = i + 1;
                        nextValS = i + 1;
                        nextValE = i + 1;

                    }
                //pretty much any other value is ok.
                }
            }


        }

        return data;

    }
    //Used for parsing out the GNF_Protein_Box template from the data.
    private String[] find_Template(String text, String title) {



        String[] dataArray = new String[3];


        //Wikipedia is caps sensitive, so we should probably be too..
        int indexS;
        int count = 0;
        int indexE = -1;
        if ((indexS = text.indexOf("{{" + title)) != -1) {
            //indexS now has the start location of the template.
            count = 1;
            for (int i = indexS + 2; i < text.length() - 2; i++) {
                String tmp = text.substring(i, i + 2);
                if (tmp.compareTo("{{") == 0) {
                    count++;
                    i++;
                }
                if (tmp.compareTo("}}") == 0) {
                    count--;
                    i++;
                }
                if (count == 0) {
                    //end of template.
                    indexE = i + 1;
                    break;
                }
            }
            if (indexE > indexS) {
                dataArray = new String[3];
                dataArray[0] = text.substring(0, indexS);
                dataArray[1] = text.substring(indexS, indexE);
                dataArray[2] = text.substring(indexE);


                return dataArray;
            }


        } else {
            //Didin't find the index.  Return null as an error.
            return null;
        }

        return null;

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        tmMain myMain = new tmMain("c:/tmover/testList.txt");
        System.out.println("Done.");
    }
}
